///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_MAT_DETAIL_CBLOCK_H_
#define _BUOLA_MAT_DETAIL_CBLOCK_H_

#include <buola/mat/cexpr.h>
#include <buola/mat/detail/csizeinfo.h>

namespace buola { namespace mat { namespace detail {

static const size_t cFullSpan=(size_t)-3;
    
template<typename tSub,size_t vRows,size_t vCols,bool vFullRows,bool vFullCols>
struct MMatrixTraits<CBlock<tSub,vRows,vCols,vFullRows,vFullCols>>
{
private:
    typedef MMatrixTraits<ODecay<tSub>> TSubTraits;

public:
    typedef typename TSubTraits::TScalar TScalar;
    
    static const size_t cRows=vRows;
    static const size_t cCols=vCols;
    static const bool cRowLinearAccess=(TSubTraits::cRowLinearAccess&&(vFullRows||vRows==1))||
                                       (TSubTraits::cColLinearAccess&&vCols==1);
    static const bool cColLinearAccess=(TSubTraits::cColLinearAccess&&(vFullCols||vCols==1))||
                                       (TSubTraits::cRowLinearAccess&&vRows==1);
    static const bool cRowPacketAccess=TSubTraits::cRowPacketAccess&&cRowLinearAccess;
    static const bool cColPacketAccess=TSubTraits::cColPacketAccess&&cColLinearAccess;
    static const bool cMightAlias=true;
    static const int cElementAccess=TSubTraits::cElementAccess;
    static const bool cEigenMappable=false; ///\todo maybe it should be mappable some times
    static const bool cKeepConstness=false;
    static const bool cExprAssignable=detail::QAssignable<tSub>::value;
    static const bool cIsMatrix=false;
};

template<bool vFullRows,bool vFullCols>
class CBlockInfo;

template<>
class CBlockInfo<false,false>
{
public:
    CBlockInfo(size_t pFirstRow,size_t pFirstCol)
        :   mFirstRow(pFirstRow)
        ,   mFirstCol(pFirstCol)
    {}
    
    size_t FirstRow() const {   return mFirstRow;   }
    size_t FirstCol() const {   return mFirstCol;   }

private:
    size_t mFirstRow,mFirstCol;
};

template<>
class CBlockInfo<true,false>
{
public:
    CBlockInfo(size_t pFirstRow,size_t pFirstCol)
        :   mFirstRow(pFirstRow)
    {
        assert(pFirstCol==0);
    }

    size_t FirstRow() const {   return mFirstRow;   }
    size_t FirstCol() const {   return 0;   }

private:
    size_t mFirstRow;
};

template<>
class CBlockInfo<false,true>
{
public:
    CBlockInfo(size_t pFirstRow,size_t pFirstCol)
        :   mFirstCol(pFirstCol)
    {
        assert(pFirstRow==0);
    }

    size_t FirstRow() const {   return 0;   }
    size_t FirstCol() const {   return mFirstCol;   }

private:
    size_t mFirstCol;
};

template<>
class CBlockInfo<true,true>
{
public:
    CBlockInfo(size_t pFirstRow,size_t pFirstCol)
    {
        assert(pFirstRow==0);
        assert(pFirstCol==0);
    }

    size_t FirstRow() const {   return 0;   }
    size_t FirstCol() const {   return 0;   }
};

//totally fixed size version (no full rows or columns)
template<typename tSub,size_t vRows,size_t vCols,bool vFullRows,bool vFullCols>
class CBlock : public CExpr<CBlock<tSub,vRows,vCols,vFullRows,vFullCols>>
             , private CSizeInfo<vRows,vCols>
             , private CBlockInfo<vFullRows,vFullCols>
{
    typedef CExpr<CBlock<tSub,vRows,vCols,vFullRows,vFullCols>> TExpr;
    typedef MMatrixTraits<CBlock<tSub,vRows,vCols,false,false>> TSelfTraits;
    typedef CSizeInfo<vRows,vCols> TSizeInfo;
    typedef CBlockInfo<vFullRows,vFullCols> TBlockInfo;

public:
    CBlock(tSub pSub,size_t pRows,size_t pCols,size_t pFirstRow,size_t pFirstCol)
        :   TSizeInfo(pRows,pCols)
        ,   TBlockInfo(pFirstRow,pFirstCol)
        ,   mSub(pSub)
    {}

    CBlock(const CBlock&)=delete;

    using TSizeInfo::Rows;
    using TSizeInfo::Cols;
    using TSizeInfo::size;
    
    template<typename tType>
    tType At(size_t r,size_t c) const   {   return mSub.template At<tType>(r+this->FirstRow(),c+this->FirstCol()); }

private:
    template<typename tType>
    tType AtHelper(size_t i,std::true_type) const  {   return At<tType>(0,i); }
    template<typename tType>
    tType AtHelper(size_t i,std::false_type) const {   return At<tType>(i,0); }
 
public:
    template<typename tType>
    tType At(size_t i) const  {   return AtHelper<tType>(i,QBool<vRows==1>()); }

    void operator=(OScalar<tSub> pScalar) const
    {
        static_assert(QElementAccess<tSub,ACCESS_ONCE>::value,"can't assign block without element access");
        
        int lRows=Rows();
        int lCols=Cols();
        for(int r=0;r<lRows;r++)
            for(int c=0;c<lCols;c++)
                At<OScalar<tSub>&>(r,c)=pScalar;
    }
    
    using TExpr::operator=;

    CBlock& operator=(const CBlock &pRH)
    {
        detail::assign<OScalar<tSub>>(*this,pRH);
        return *this;
    }

private:
    tSub mSub;
};

/*namespace detail*/ } /*namespace mat*/ } /*namespace buola*/ }

#endif
